﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using PacMan.Components;

namespace MiniPac.Components
{
	//TODO:
	//Fixa fram-å-tillbaka gunget..

	class Player : IGameComponent
	{
		//Maze
		protected Maze maze;

		//Update vals
		protected Input input;
		protected Input lastInput;
		protected double lastUpdate = 0;
		protected double nextUpdate;
		protected double freq = 30;
		protected double vel = R.pVel;
		public int points;

		public bool IsDead;

		//Abstract coordinates
		protected double AbstractX;
		protected double AbstractY;

		//Screen coordinates
		public int ScreenX { get { return (int)AbstractX; } }
		public int ScreenY { get { return (int)AbstractY; } }

		//Directions
		protected Dir currDir;
		protected Dir nextDir;
		protected bool IsMoving { get { return currDir != Dir.Still; } }
		public Dir Dir { get { return currDir; } }

		//Sprite colour
		protected Color color;

		//init with exact values...
		public Player(Maze m, double x, double y, Color c)
		{
			AbstractX = x;
			AbstractY = y;
			color = c;
			currDir = Dir.Still;
			nextDir = Dir.Still;
			maze = m;
			lastInput = InputHandler.UpdateInput(0);
		}

		//init on stage, with account to margins...
		public Player(Maze m, int x, int y, Color c)
		{
			AbstractX = R.MarginX + R.Size * x;
			AbstractY = R.MarginY + R.Size * y;
			color = c;
			currDir = Dir.Still;
			nextDir = Dir.Still;
			maze = m;
			lastInput = InputHandler.UpdateInput(0);

		}

		//Goto to pacman start pos...
		public void ResetPos()
		{
			AbstractX = R.MarginX + R.Size * (((double)R.MazeXSize) / 2) - ((double)R.Size) / 2;
			AbstractY = R.MarginY + R.Size * 23;
		}

		//Player Bounding box, used perhaps for Player/Maze collision...
		public Rectangle BB
		{
			get { return new Rectangle(ScreenX, ScreenY, R.Size, R.Size); }
		}

		public virtual void Draw(SpriteBatch sb)
		{

			sb.Draw(R.Player, BB, color);

#if DEBUG
			sb.DrawString(R.c64_font, "plr:" + AbstractX + " " + AbstractY, new Vector2(R.StageSizeX, 25), Color.White);
			sb.DrawString(R.c64_font, "X:" + R.MazeXSize + " Y:" + R.MazeYSize, new Vector2(R.StageSizeX, 40), Color.White);
			sb.DrawString(R.c64_font, "tile:" + GetCurrentTile().ToString(), new Vector2(R.StageSizeX, 55), Color.White);
			sb.DrawString(R.c64_font, "currDir:" + currDir.ToString(), new Vector2(R.StageSizeX, 70), Color.White);
			sb.DrawString(R.c64_font, "nextDir:" + nextDir.ToString(), new Vector2(R.StageSizeX, 85), Color.White);
			sb.DrawString(R.c64_font, "mX:" + R.MazeArray.GetLength(0) + " mY:" + R.MazeArray.GetLength(1), new Vector2(R.StageSizeX, 100), Color.White);
			sb.DrawString(R.c64_font, "timer:" + State.scaredTimer, new Vector2(R.StageSizeX, 115), Color.White);
#endif
#if DEBUG__ 
			sb.DrawString(R.c64_font, "Points:" + points.ToString(), new Vector2(R.StageSizeX, 115), Color.White);

#endif

		}

		public virtual void Update(double time)
		{
			input = InputHandler.UpdateInput(time);

			if (input.up)
				{
					if (IsMoving)
					{
						nextDir = Dir.Up;
					}
					else
					{
						currDir = Dir.Up;
						nextDir = Dir.Still;
					}
				}
				else if (input.right)
				{
					if (IsMoving)
					{
						nextDir = Dir.Right;
					}
					else
					{
						currDir = Dir.Right;
						nextDir = Dir.Still;
					}
				}
				else if (input.down)
				{
					if (IsMoving)
					{
						nextDir = Dir.Down;
					}
					else
					{
						currDir = Dir.Down;
						nextDir = Dir.Still;
					}
				}
				else if (input.left)
				{
					if (IsMoving)
					{
						nextDir = Dir.Left;
					}
					else
					{
						currDir = Dir.Left;
						nextDir = Dir.Still;
					}
				}

				if (StepsOverCentre(currDir)) // is over the centre
				{
					if (GetNextTileType(nextDir) >= 5) // if not wall
					{
						currDir = nextDir;
						nextDir = Dir.Still;
						AllignToTile();
						Tile ct = GetTile(GetCurrentTile());
						if (ct.Type == 6)
						{
							State.pnts++;
							PlayEatDot();
							ct.Type = 5;
						}
						if (ct.Type == 9)
						{
							State.pnts += 3;
							ct.Type = 5;
							State.scaredGhosts = true;
						}
					}
					else if (GetNextTileType(currDir) >= 5)
					{
						UpdatePos(currDir, vel);
						Tile ct = GetTile(GetCurrentTile());
						if (ct.Type == 6)
						{
							State.pnts++;
							PlayEatDot();
							ct.Type = 5;
						}
						if (ct.Type == 9)
						{
							State.pnts += 3;
							ct.Type = 5;
							State.scaredGhosts = true;
						}
					}
					else
					{
						currDir = nextDir = Dir.Still;
						AllignToTile();
						Tile ct = GetTile(GetCurrentTile());
						if (ct.Type == 6) 
						{
							State.pnts++;
							PlayEatDot();
							ct.Type = 5; 
						}
						if (ct.Type == 9)
						{
							State.pnts += 3;
							ct.Type = 5;
							State.scaredGhosts = true;
						}
					}
				}
				
				UpdatePos(currDir, vel);

				#region Special case for "the teleport"...

				int type = GetTile(GetCurrentTile()).Type;

				if (type == 7 && currDir == Dir.Left)
				{
					SetCurrTile(R.MazeXSize - 1, 14);
				}
				else if (type == 8 && currDir == Dir.Right)
				{
					SetCurrTile(0, 14);
				}

				#endregion

		}

		//Updates position according to current direction
		public void UpdatePos(Dir dir, double vel)
		{
			switch (dir)
			{
				case Dir.Up:
					AbstractY -= vel;
					break;
				case Dir.Right:
					AbstractX += vel;
					break;
				case Dir.Down:
					AbstractY += vel;
					break;
				case Dir.Left:
					AbstractX -= vel;
					break;
			}
		}

		//Checks if player steps on/over the center of the current tile
		protected bool StepsOverCentre(Dir dir)
		{
			Tile cTile = GetTile(GetCurrentTile());
			bool res;

			switch (dir)
			{
				case Dir.Up:
					AbstractY -= R.pVel;
					res = BB.Top < cTile.BB.Top;
					AbstractY += R.pVel;
					return res;
				case Dir.Right:
					AbstractX += R.pVel;
					res = BB.Right > cTile.BB.Right;
					AbstractX -= R.pVel;
					return res;
				case Dir.Down:
					AbstractY += R.pVel;
					res = BB.Bottom > cTile.BB.Bottom;
					AbstractY -= R.pVel;
					return res;
				case Dir.Left:
					AbstractX -= R.pVel;
					res = BB.Left < cTile.BB.Left;
					AbstractX += R.pVel;
					return res;
				default:
					return false;
			}
		}

		// centers the player on current tile
		protected void AllignToTile()
		{
			Point p = GetCurrentTile();
			SetCurrTile(p.X, p.Y);
		}

		protected void CenterX()
		{
			AbstractX = R.MarginX + (R.MazeXSize * R.Size) / 2d - (R.Size / 2d) + 1;
		}

		// checks if tile is wall or something else...
		public int GetNextTileType(Dir dir)
		{
			Point curr = GetCurrentTile();

			switch (dir)
			{
				case Dir.Up:
					curr.Y--;
					break;
				case Dir.Right:
					curr.X++;
					break;
				case Dir.Down:
					curr.Y++;
					break;
				case Dir.Left:
					curr.X--;
					break;
				default:
					return -1;
			}
			return GetTile(curr).Type;
		}

		public Point GetNextTile(Dir dir)
		{
			Point curr = GetCurrentTile();

			switch (dir)
			{
				case Dir.Up:
					curr.Y--;
					break;
				case Dir.Right:
					curr.X++;
					break;
				case Dir.Down:
					curr.Y++;
					break;
				case Dir.Left:
					curr.X--;
					break;
				default:
					break;
			}
			return curr;
		}

		public bool IsWall(Dir dir)
		{
			Point curr = GetCurrentTile();

			switch (dir)
			{
				case Dir.Up:
					curr.Y--;
					break;
				case Dir.Right:
					curr.X++;
					break;
				case Dir.Down:
					curr.Y++;
					break;
				case Dir.Left:
					curr.X--;
					break;
				default:
					return false;
			}

			return GetTile(curr).Type <= R.Some_Kind_Of_Wall;
		}

		public bool IsWall(Point p)
		{
			return GetTile(p).Type <= R.Some_Kind_Of_Wall;
		}

		public bool IsWall(int x, int y)
		{
			return GetTile(new Point(x, y)).Type <= R.Some_Kind_Of_Wall;
		}



		//gets tile at specified coords
		public Tile GetTile(Point p)
		{
			return maze[p.X, p.Y];
		}

		//Returns tile matrix-coordiates of the current tile
		public Point GetCurrentTile()
		{
			int x = (BB.Center.X - R.MarginX) / R.Size;
			int y = (BB.Center.Y - R.MarginY) / R.Size;
			return new Point(x, y);
		}

		// centers the player on given tile
		public void SetCurrTile(int tX, int tY)
		{
			Tile t = maze[tX, tY];

			this.AbstractX = t.BB.X;
			this.AbstractY = t.BB.Y;
		}

		private void PlayEatDot()
		{
			if ((R.snd_eat_dot_istance.State == SoundState.Stopped))
			{
				R.snd_eat_dot_istance.Play();
			}
		}

	}

}
